<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="keywords" content="Hexo Theme Keep">
    <meta name="description" content="宋标的个人博客">
    <meta name="author" content="宋标">
	<meta name="referrer" content="no-referrer"/>
    
    <title>
        
            设计模式SOLID原则 |
        
        宋标的blog
    </title>
    
<link rel="stylesheet" href="/css/style.css">

    <link rel="shortcut icon" href="/images/logo.svg">
    
<link rel="stylesheet" href="/css/font-awesome.min.css">

    <script id="hexo-configurations">
    let KEEP = window.KEEP || {};
    KEEP.hexo_config = {"hostname":"song_biao.gitee.io","root":"/","language":"zh-CN","path":"search.xml"};
    KEEP.theme_config = {"toc":{"enable":true,"number":true,"expand_all":true,"init_open":true},"style":{"primary_color":"#0066CC","avatar":"/images/avatar.svg","favicon":"/images/logo.svg","article_img_align":"left","left_side_width":"260px","content_max_width":"920px","hover":{"shadow":false,"scale":false},"first_screen":{"enable":true,"background_img":"/images/bg.svg","description":"while(alive()) study();"},"scroll":{"progress_bar":{"enable":false},"percent":{"enable":false}}},"local_search":{"enable":true,"preload":false},"code_copy":{"enable":false,"style":"default"},"pjax":{"enable":false},"lazyload":{"enable":false},"version":"3.4.5"};
    KEEP.language_ago = {"second":"%s 秒前","minute":"%s 分钟前","hour":"%s 小时前","day":"%s 天前","week":"%s 周前","month":"%s 个月前","year":"%s 年前"};
  </script>
<meta name="generator" content="Hexo 6.1.0"></head>


<body>
<div class="progress-bar-container">
    

    
</div>


<main class="page-container">

    

    <div class="page-main-content">

        <div class="page-main-content-top">
            <header class="header-wrapper">

    <div class="header-content">
        <div class="left">
            
            <a class="logo-title" href="/">
                宋标的blog
            </a>
        </div>

        <div class="right">
            <div class="pc">
                <ul class="menu-list">
                    
                        <li class="menu-item">
                            <a class=""
                               href="/"
                            >
                                首页
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/tags"
                            >
                                标签
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/categories"
                            >
                                分类
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/archives"
                            >
                                归档
                            </a>
                        </li>
                    
                    
                        <li class="menu-item search search-popup-trigger">
                            <i class="fas fa-search"></i>
                        </li>
                    
                </ul>
            </div>
            <div class="mobile">
                
                    <div class="icon-item search search-popup-trigger"><i class="fas fa-search"></i></div>
                
                <div class="icon-item menu-bar">
                    <div class="menu-bar-middle"></div>
                </div>
            </div>
        </div>
    </div>

    <div class="header-drawer">
        <ul class="drawer-menu-list">
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/">首页</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/tags">标签</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/categories">分类</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/archives">归档</a>
                </li>
            
        </ul>
    </div>

    <div class="window-mask"></div>

</header>


        </div>

        <div class="page-main-content-middle">

            <div class="main-content">

                
                    <div class="fade-in-down-animation">
    <div class="article-content-container">

        <div class="article-title">
            <span class="title-hover-animation">设计模式SOLID原则</span>
        </div>

        
            <div class="article-header">
                <div class="avatar">
                    <img src="/images/avatar.svg">
                </div>
                <div class="info">
                    <div class="author">
                        <span class="name">宋标</span>
                        
                            <span class="author-label">Lv5</span>
                        
                    </div>
                    <div class="meta-info">
                        <div class="article-meta-info">
    <span class="article-date article-meta-item">
        <i class="fas fa-edit"></i>&nbsp;
        <span class="pc">2022-10-31 00:55:10</span>
        <span class="mobile">2022-10-31 00:55</span>
    </span>
    
        <span class="article-categories article-meta-item">
            <i class="fas fa-folder"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/">设计模式</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    
    
        <span class="article-tags article-meta-item">
            <i class="fas fa-tags"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/tags/SOLID%E5%8E%9F%E5%88%99/">SOLID原则</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    

    
    
    
    
</div>

                    </div>
                </div>
            </div>
        

        <div class="article-content markdown-body">
            <meta name="referrer" content="no-referrer" />

<h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>SOLID原则是一种编码的标准，为了避免不良设计，所有的软件开发人员都应该清楚这些原则。SOLID原则是由Robert C Martin推广并被广泛引用于面向对象编程中。正确使用这些规范将提升你的代码的可扩展性、逻辑性和可读性。</p>
<p>当开发人员按照不好的设计来开发软件时，代码将失去灵活性和健壮性。任何一点点小的修改都非常容易引起bug。因此，我们应该遵循SOLID原则。</p>
<p>首先我们需要花一些时间来了解SOLID原则，当你能够理解这些原则并正确使用时，你的代码质量将会得到大幅的提高。同时，它可以帮助你更好的理解一些优秀软件的设计。</p>
<p>为了理解SOLID原则，你必须清楚接口的用法，如果你还不理解接口的概念，建议你先读一读这篇<a class="link"   target="_blank" rel="noopener" href="https://medium.com/better-programming/understanding-use-of-interface-and-abstract-class-9a82f5f15837"  title="https://medium.com/better-programming/understanding-use-of-interface-and-abstract-class-9a82f5f15837">文章<i class="fas fa-external-link-alt"></i></a>。</p>
<p>下面我将用简单易懂的方式为你描述SOLID原则，希望能帮助你对这些原则有个初步的理解。</p>
<h3 id="单一责任原则"><a href="#单一责任原则" class="headerlink" title="单一责任原则"></a>单一责任原则</h3><blockquote>
<p>一个类只能因为一个理由被修改。</p>
<p><em>A class should have one, and only one, reason to change.</em></p>
</blockquote>
<p>一个类应该只为一个目标服务。并不是说每个类都只能有一个方法，但它们都应该与类的责任有直接关系。所有的方法和属性都应该努力做好同一类事情。当一个类具有多个目标或职责时，就应该创建一个新的类出来。</p>
<p>我们来看一下这段代码：</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrdersReportService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">List</span>&lt;<span class="title class_">OrderVO</span>&gt; <span class="title function_">getOrdersInfo</span>(<span class="params"><span class="built_in">Date</span> startDate, <span class="built_in">Date</span> endDate</span>) &#123;</span><br><span class="line">        <span class="title class_">List</span>&lt;<span class="title class_">OrderDO</span>&gt; orders = <span class="title function_">queryDBForOrders</span>(startDate, endDate);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="title function_">transform</span>(orders);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title class_">List</span>&lt;<span class="title class_">OrderDO</span>&gt; <span class="title function_">queryDBForOrders</span>(<span class="params"><span class="built_in">Date</span> startDate, <span class="built_in">Date</span> endDate</span>) &#123;</span><br><span class="line">        <span class="comment">// select * from order where date &gt;= startDate and date &lt; endDate;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title class_">List</span>&lt;<span class="title class_">OrderVO</span>&gt; <span class="title function_">transform</span>(<span class="params">List&lt;OrderDO&gt; orderDOList</span>) &#123;</span><br><span class="line">        <span class="comment">//transform DO to VO</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这段代码就违反了单一责任原则。为什么会在这个类中执行sql语句？这样的操作应该放到持久化层，持久化层负责处理数据的持久化的相关操作，包括从数据库中存储或查询数据。所以这个职责不应该属于这个类。</p>
<p>transform方法同样不应该属于这个类，因为我们可能需要很多种类型的转换。</p>
<p>因此我们需要对代码进行重构，重构之后的代码如下（为了节省篇幅）：</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrdersReportService</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title class_">OrdersReportDao</span> ordersReportDao;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title class_">Formatter</span> formatter;</span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">List</span>&lt;<span class="title class_">OrderVO</span>&gt; <span class="title function_">getOrdersInfo</span>(<span class="params"><span class="built_in">Date</span> startDate, <span class="built_in">Date</span> endDate</span>) &#123;</span><br><span class="line">        <span class="title class_">List</span>&lt;<span class="title class_">OrderDO</span>&gt; orders = ordersReportDao.<span class="title function_">queryDBForOrders</span>(startDate, endDate);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> formatter.<span class="title function_">transform</span>(orders);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OrdersReportDao</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">List</span>&lt;<span class="title class_">OrderDO</span>&gt; <span class="title function_">queryDBForOrders</span>(<span class="params"><span class="built_in">Date</span> startDate, <span class="built_in">Date</span> endDate</span>) &#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Formatter</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="title class_">List</span>&lt;<span class="title class_">OrderVO</span>&gt; <span class="title function_">transform</span>(<span class="params">List&lt;OrderDO&gt; orderDOList</span>) &#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="开闭原则"><a href="#开闭原则" class="headerlink" title="开闭原则"></a>开闭原则</h3><blockquote>
<p>对扩展开放，对修改关闭。</p>
<p><em>Entities should be open for extension, but closed for modification.</em></p>
</blockquote>
<p>软件实体（包括类、模块、函数等）都应该可扩展，而不用因为扩展而修改实体的内容。如果我们严格遵循这个原则，就可以做到修改代码行为时，不需要改动任何原始代码。</p>
<p>我们还是以一段代码为例：</p>
<figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Rectangle</span> extends Shape &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> width;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> height;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Rectangle</span><span class="params">(<span class="type">int</span> width, <span class="type">int</span> height)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.width = width;</span><br><span class="line">        <span class="keyword">this</span>.height = height;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Circle</span> extends Shape &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> radius;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Circle</span><span class="params">(<span class="type">int</span> radius)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.radius = radius;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CostManager</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="type">double</span> <span class="title">calculate</span><span class="params">(Shape shape)</span> </span>&#123;</span><br><span class="line">        <span class="type">double</span> costPerUnit = <span class="number">1.5</span>;</span><br><span class="line">        <span class="type">double</span> area;</span><br><span class="line">        <span class="keyword">if</span> (shape instanceof Rectangle) &#123;</span><br><span class="line">            area = shape.<span class="built_in">getWidth</span>() * shape.<span class="built_in">getHeight</span>();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            area = shape.<span class="built_in">getRadius</span>() * shape.<span class="built_in">getRadius</span>() * <span class="built_in">pi</span>();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> costPerUnit * area;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果你想要计算正方形的面积，那么我们就需要修改calculate方法的代码。这就破坏了开闭原则。根据这个原则，我们不能修改原有代码，但是我们可以进行扩展。</p>
<p>所以我们可以把计算面积的方法放到Shape类中，再由每个继承它的子类自己去实现自己的计算方法。这样就不用修改原有的代码了。</p>
<h3 id="里氏替换原则"><a href="#里氏替换原则" class="headerlink" title="里氏替换原则"></a>里氏替换原则</h3><p>里氏替换原则是由<a class="link"   target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Barbara_Liskov"  title="https://en.wikipedia.org/wiki/Barbara_Liskov">Barbara Liskov<i class="fas fa-external-link-alt"></i></a>在1987年的“数据抽象“大会上提出的。Barbara Liskov和<a class="link"   target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Jeannette_Wing"  title="https://en.wikipedia.org/wiki/Jeannette_Wing">Jeannette Wing<i class="fas fa-external-link-alt"></i></a>在1994年发表了论文对这一原则进行阐述：</p>
<blockquote>
<p>如果φ(x)是类型T的属性，并且S是T的子类型，那么φ(y)就是S的属性。</p>
<p><em>Let φ(x) be a property provable about objects x of type T. Then φ(y) should be true for objects y of type S where S is a subtype of T.</em></p>
</blockquote>
<p>Barbara Liskov给出了易于理解的版本，但是这一版本更依赖于类型系统：</p>
<blockquote>
<p><em>1. Preconditions cannot be strengthened in a subtype.</em> <em>2. Postconditions cannot be weakened in a subtype.</em> <em>3. Invariants of the supertype must be preserved in a subtype.</em></p>
</blockquote>
<p>Robert Martin在1996年提出了更加简洁、通顺的定义：</p>
<blockquote>
<p>使用指向基类指针的函数也可以使用子类。</p>
<p><em>Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it.</em></p>
</blockquote>
<p>更简单一点讲就是子类可以替代父类。</p>
<p>根据里氏替换原则，我们可以在接受抽象类（接口）的任何地方用它的子类（实现类）来替代它们。基本上，我们应该注意在编程时不能只关注接口的输入参数，还需要保证接口实现类的返回值都是同一类型的。</p>
<p>下面这段代码就违反了里氏替换原则：</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">LessonRepositoryInterface</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Fetch all records.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> array</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getAll</span>(<span class="params"></span>)</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileLessonRepository</span> <span class="keyword">implements</span> <span class="title">LessonRepositoryInterface</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getAll</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// return through file system</span></span><br><span class="line">        <span class="keyword">return</span> [];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">DbLessonRepository</span> <span class="keyword">implements</span> <span class="title">LessonRepositoryInterface</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">getAll</span>(<span class="params"></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">            Violates LSP because:</span></span><br><span class="line"><span class="comment">              - the return type is different</span></span><br><span class="line"><span class="comment">              - the consumer of this subclass and FileLessonRepository won&#x27;t work identically</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="comment">// return Lesson::all();</span></span><br><span class="line">        <span class="comment">// to fix this</span></span><br><span class="line">        <span class="keyword">return</span> <span class="title class_">Lesson</span>::<span class="title function_ invoke__">all</span>()-&gt;<span class="title function_ invoke__">toArray</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>译者注：这里没想到Java应该怎么实现，因此直接用了作者的代码，大家理解就好</p>
<h3 id="接口隔离原则"><a href="#接口隔离原则" class="headerlink" title="接口隔离原则"></a>接口隔离原则</h3><blockquote>
<p>不能强制客户端实现它不使用的接口。</p>
<p><em>A client should not be forced to implement an interface that it doesn’t use.</em></p>
</blockquote>
<p>这个规则告诉我们，应该把接口拆的尽可能小。这样才能更好的满足客户的确切需求。</p>
<p>与单一责任原则类似，接口隔离原则也是通过将软件拆分为多个独立的部分来最大程度的减少副作用和重复代码。</p>
<p>我们来看一个例子：</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">WorkerInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">work</span>()</span>;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">sleep</span>()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">HumanWorker</span> <span class="title">implements</span> <span class="title">WorkerInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">work</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;work&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sleep</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;sleep&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">RobotWorker</span> <span class="title">implements</span> <span class="title">WorkerInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">work</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;work&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sleep</span>()</span> &#123;</span><br><span class="line">        <span class="comment">// No need</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面这段代码中，我们很容易发现问题所在，机器人不需要睡觉，但是由于实现了WorkerInterface接口，它不得不实现sleep方法。这就违背了接口隔离的原则，下面我们一起修复一下这段代码：</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">WorkAbleInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">work</span>()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">SleepAbleInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">sleep</span>()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">HumanWorker</span> <span class="title">implements</span> <span class="title">WorkAbleInterface</span>, <span class="title">SleepAbleInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">work</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;work&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sleep</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;sleep&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">RobotWorker</span> <span class="title">implements</span> <span class="title">WorkerInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">work</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;work&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="依赖倒置原则"><a href="#依赖倒置原则" class="headerlink" title="依赖倒置原则"></a>依赖倒置原则</h3><blockquote>
<p>高层模块不应该依赖于低层的模块，它们都应该依赖于抽象。</p>
<p>抽象不应该依赖于细节，细节应该依赖于抽象。</p>
<p>High-level modules should not depend on low-level modules. Both should depend on abstractions.</p>
<p>Abstractions should not depend on details. Details should depend on abstractions.</p>
</blockquote>
<p>简单来讲就是：抽象不依赖于细节，而细节依赖于抽象。</p>
<p>通过应用依赖倒置模块，只需要修改依赖模块，其他模块就可以轻松得到修改。同时，低层模块的修改是不会影响到高层模块修改的。</p>
<p>我们来看这段代码：</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MySQLConnection</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">connect</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;MYSQL Connection&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">PasswordReminder</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> MySQLConnection mySQLConnection;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PasswordReminder</span>(<span class="params">MySQLConnection mySQLConnection</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>.mySQLConnection = mySQLConnection;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>有一种常见的误解是，依赖倒置只是依赖注入的另一种表达方式，实际上两者并不相同。</p>
<p>在上面这段代码中，尽管将MySQLConnection类注入了PasswordReminder类，但它依赖于MySQLConnection。而高层模块PasswordReminder是不应该依赖于低层模块MySQLConnection的。因此这不符合依赖倒置原则。</p>
<p>如果你想要把MySQLConnection改成MongoConnection，那就要在PasswordReminder中更改硬编码的构造函数注入。</p>
<p>要想符合依赖倒置原则，PasswordReminder就要依赖于抽象类（接口）而不是细节。那么应该怎么改这段代码呢？我们一起来看一下：</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">ConnectionInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">connect</span>()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MySQLConnection</span> <span class="title">implements</span> <span class="title">ConnectionInterface</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">connect</span>()</span> &#123;</span><br><span class="line">        System.<span class="keyword">out</span>.println(<span class="string">&quot;MYSQL Connection&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">PasswordReminder</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> ConnectionInterface connection;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PasswordReminder</span>(<span class="params">ConnectionInterface connection</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>.connection = connection;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>修改后的代码中，如果我们想要将MySQLConnection改成MongoConnection，就不需要修改PasswordReminder类的构造函数注入，因为这里PasswordReminder类依赖于抽象而非细节。</p>
<p>感谢阅读！</p>
<h3 id="原文地址"><a href="#原文地址" class="headerlink" title="原文地址"></a>原文地址</h3><p><a class="link"   target="_blank" rel="noopener" href="https://medium.com/better-programming/solid-principles-simple-and-easy-explanation-f57d86c47a7f"  title="https://medium.com/better-programming/solid-principles-simple-and-easy-explanation-f57d86c47a7f">medium.com&#x2F;better-prog…<i class="fas fa-external-link-alt"></i></a></p>
<h3 id="译者点评"><a href="#译者点评" class="headerlink" title="译者点评"></a>译者点评</h3><p>作者对于SOLID原则介绍的还是比较清楚的，但是里氏原则那里我认为说得还不是很明白，举的例子似乎也不是很明确。我理解的里氏替换原则是：子类可以扩展父类的功能，但不能修改父类方法。因此里氏替换原则可以说是开闭原则的一种实现。当然，这篇文章也只是大概介绍了SOLID的每个原则，大家可以通过查资料来进行更详细的了解。我相信理解了这些设计原则之后，你对程序设计就会有更加深入的认识。后面我也会继续推送一些关于设计原则的文章，欢迎关注。</p>

        </div>

        

        
            <ul class="post-tags-box">
                
                    <li class="tag-item">
                        <a href="/tags/SOLID%E5%8E%9F%E5%88%99/">#SOLID原则</a>&nbsp;
                    </li>
                
            </ul>
        

        
            <div class="article-nav">
                
                    <div class="article-prev">
                        <a class="prev"
                           rel="prev"
                           href="/2023/02/23/bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/"
                        >
                            <span class="left arrow-icon flex-center">
                              <i class="fas fa-chevron-left"></i>
                            </span>
                            <span class="title flex-center">
                                <span class="post-nav-title-item">bean的生命周期</span>
                                <span class="post-nav-item">上一篇</span>
                            </span>
                        </a>
                    </div>
                
                
                    <div class="article-next">
                        <a class="next"
                           rel="next"
                           href="/2022/10/20/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%8C%83%E5%BC%8F/"
                        >
                            <span class="title flex-center">
                                <span class="post-nav-title-item">数据库范式</span>
                                <span class="post-nav-item">下一篇</span>
                            </span>
                            <span class="right arrow-icon flex-center">
                              <i class="fas fa-chevron-right"></i>
                            </span>
                        </a>
                    </div>
                
            </div>
        

        
            <div class="comment-container">
                <div class="comments-container">
    <div id="comment-anchor"></div>
    <div class="comment-area-title">
        <i class="fas fa-comments">&nbsp;评论</i>
    </div>
    

        
            
    <div class="valine-container">
        <script 
                src="//cdn.jsdelivr.net/npm/valine@latest/dist/Valine.min.js"></script>
        <div id="vcomments"></div>
        <script >
            function loadValine() {
                new Valine({
                    el: '#vcomments',
                    appId: 'GaPFQduCGtJWQ2Cmma9jEfx1-gzGzoHsz',
                    appKey: 'h9IWwxU3RWqBFCD4CqERkpuv',
                    meta: ['nick', 'mail', 'link'],
                    avatar: 'wavatar',
                    enableQQ: true,
                    placeholder: '',
                    lang: 'zh-CN'.toLowerCase()
                });

                function getAuthor(language) {
                    switch (language) {
                        case 'en':
                            return 'Author';
                        case 'zh-CN':
                            return '博主';
                        default:
                            return 'Master';
                    }
                }

                // Add "Author" identify
                const getValineDomTimer = setInterval(() => {
                    const vcards = document.querySelectorAll('#vcomments .vcards .vcard');
                    if (vcards.length > 0) {
                        let author = '宋标';

                        if (author) {
                            for (let vcard of vcards) {
                                const vnick_dom = vcard.querySelector('.vhead .vnick');
                                const vnick = vnick_dom.innerHTML;
                                if (vnick === author) {
                                    vnick_dom.innerHTML = `${vnick} <span class="author">${getAuthor(KEEP.hexo_config.language)}</span>`
                                }
                            }
                        }
                        clearInterval(getValineDomTimer);
                    } else {
                        clearInterval(getValineDomTimer);
                    }
                }, 2000);
            }

            if ('false') {
                const loadValineTimeout = setTimeout(() => {
                    loadValine();
                    clearTimeout(loadValineTimeout);
                }, 1000);
            } else {
                window.addEventListener('DOMContentLoaded', loadValine);
            }
        </script>
    </div>



        
    
</div>

            </div>
        
    </div>
</div>


                
            </div>

        </div>

        <div class="page-main-content-bottom">
            <footer class="footer">
    <div class="info-container">
        <div class="copyright-info info-item">
            &copy;
            
              <span>2020</span>
              -
            
            2023&nbsp;<i class="fas fa-heart icon-animate"></i>&nbsp;<a href="/">宋标</a>
        </div>
        
        <div class="theme-info info-item">
            由 <a target="_blank" href="https://hexo.io">Hexo</a> 驱动&nbsp;|&nbsp;主题&nbsp;<a class="theme-version" target="_blank" href="https://github.com/XPoet/hexo-theme-keep">Keep v3.4.5</a>
        </div>
        
        
    </div>
</footer>

        </div>
    </div>

    
        <div class="post-tools">
            <div class="post-tools-container">
    <ul class="tools-list">
        <!-- TOC aside toggle -->
        
            <li class="tools-item page-aside-toggle">
                <i class="fas fa-outdent"></i>
            </li>
        

        <!-- go comment -->
        
            <li class="go-comment">
                <i class="fas fa-comment"></i>
            </li>
        
    </ul>
</div>

        </div>
    

    <div class="right-bottom-side-tools">
        <div class="side-tools-container">
    <ul class="side-tools-list">
        <li class="tools-item tool-font-adjust-plus flex-center">
            <i class="fas fa-search-plus"></i>
        </li>

        <li class="tools-item tool-font-adjust-minus flex-center">
            <i class="fas fa-search-minus"></i>
        </li>

        <li class="tools-item tool-expand-width flex-center">
            <i class="fas fa-arrows-alt-h"></i>
        </li>

        <li class="tools-item tool-dark-light-toggle flex-center">
            <i class="fas fa-moon"></i>
        </li>

        <!-- rss -->
        

        
            <li class="tools-item tool-scroll-to-top flex-center">
                <i class="fas fa-arrow-up"></i>
            </li>
        

        <li class="tools-item tool-scroll-to-bottom flex-center">
            <i class="fas fa-arrow-down"></i>
        </li>
    </ul>

    <ul class="exposed-tools-list">
        <li class="tools-item tool-toggle-show flex-center">
            <i class="fas fa-cog fa-spin"></i>
        </li>
        
    </ul>
</div>

    </div>

    
        <aside class="page-aside">
            <div class="post-toc-wrap">
    <div class="post-toc">
        <ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%AE%80%E4%BB%8B"><span class="nav-number">1.</span> <span class="nav-text">简介</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8D%95%E4%B8%80%E8%B4%A3%E4%BB%BB%E5%8E%9F%E5%88%99"><span class="nav-number">2.</span> <span class="nav-text">单一责任原则</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%BC%80%E9%97%AD%E5%8E%9F%E5%88%99"><span class="nav-number">3.</span> <span class="nav-text">开闭原则</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%87%8C%E6%B0%8F%E6%9B%BF%E6%8D%A2%E5%8E%9F%E5%88%99"><span class="nav-number">4.</span> <span class="nav-text">里氏替换原则</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%8E%A5%E5%8F%A3%E9%9A%94%E7%A6%BB%E5%8E%9F%E5%88%99"><span class="nav-number">5.</span> <span class="nav-text">接口隔离原则</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BE%9D%E8%B5%96%E5%80%92%E7%BD%AE%E5%8E%9F%E5%88%99"><span class="nav-number">6.</span> <span class="nav-text">依赖倒置原则</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8E%9F%E6%96%87%E5%9C%B0%E5%9D%80"><span class="nav-number">7.</span> <span class="nav-text">原文地址</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AF%91%E8%80%85%E7%82%B9%E8%AF%84"><span class="nav-number">8.</span> <span class="nav-text">译者点评</span></a></li></ol>
    </div>
</div>
        </aside>
    

    <div class="image-viewer-container">
    <img src="">
</div>


    
        <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
          <span class="search-input-field-pre">
            <i class="fas fa-keyboard"></i>
          </span>
            <div class="search-input-container">
                <input autocomplete="off"
                       autocorrect="off"
                       autocapitalize="off"
                       placeholder="搜索..."
                       spellcheck="false"
                       type="search"
                       class="search-input"
                >
            </div>
            <span class="popup-btn-close">
                <i class="fas fa-times"></i>
            </span>
        </div>
        <div id="search-result">
            <div id="no-result">
                <i class="fas fa-spinner fa-pulse fa-5x fa-fw"></i>
            </div>
        </div>
    </div>
</div>

    

</main>




<script src="/js/utils.js"></script>

<script src="/js/main.js"></script>

<script src="/js/header-shrink.js"></script>

<script src="/js/back2top.js"></script>

<script src="/js/dark-light-toggle.js"></script>



    
<script src="/js/local-search.js"></script>







<div class="post-scripts">
    
        
<script src="/js/left-side-toggle.js"></script>

<script src="/js/libs/anime.min.js"></script>

<script src="/js/toc.js"></script>

    
</div>



</body>
</html>
